/**
 * 数据处理逻辑
 */

import { cloneDeep } from 'lodash'
import { RELATION_SHIP } from '../constants'
import { isValidArray } from './utils'

// #region 数据获取
/**
 * 获取行数据
 */
function getRowData(originData: ShuDuNS.ShuDuData, index: number) {
  return originData[index]
}

/**
 * 获取列数据
 */
function getColData(originData: ShuDuNS.ShuDuData, index: number) {
  const result = []
  for (let i = 0; i < originData.length; i++) {
    result.push(originData[i][index])
  }
  return result
}

/**
 * 获取宫格数据
 * 左上到右下依次是 1 - 9，从左到右
 */
export function getGongData(originData: ShuDuNS.ShuDuData, index: number) {
  const result = []
  const x = Math.floor(index / 3)
  const y = index % 3
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      result.push(originData[x * 3 + i][y * 3 + j])
    }
  }
  return result
}

/**
 * 返回一个完整宫的 index
 */
function getFullGongIndex() {
  const result = []
  for (let index = 0; index < 9; index++) {
    result.push(index)
  }
  return result
}

/**
 * 获取两个宫的关系，是横向还是纵向
 * 可通过两个宫的 index 的差值是否 >= 3
 * 横向返回 true，纵向返回 false
 */
function getGongRelation(gong1: number, gong2: number) {
  return Math.abs(gong1 - gong2) < 3
}

/**
 * 根据关联的宫中取到的目标数字的索引和宫之间的关系，然后进行过滤
 */
function filterRelatedGong(
  fullIndex: number[],
  targetIndex: number,
  gongRelation: boolean
) {
  // 横向
  function getHorizontal(index: number) {
    switch (index) {
      case 0:
      case 1:
      case 2:
        return [0, 1, 2]
      case 3:
      case 4:
      case 5:
        return [3, 4, 5]
      case 6:
      case 7:
      case 8:
        return [6, 7, 8]
      default:
        return []
    }
  }
  // 纵向
  function getVertical(index: number) {
    switch (index) {
      case 0:
      case 3:
      case 6:
        return [0, 3, 6]
      case 1:
      case 4:
      case 7:
        return [1, 4, 7]
      case 2:
      case 5:
      case 8:
        return [2, 5, 8]
      default:
        return []
    }
  }
  if (gongRelation) {
    return fullIndex.filter(
      (item) => !getHorizontal(targetIndex).includes(item)
    )
  }
  return fullIndex.filter((item) => !getVertical(targetIndex).includes(item))
}
// #endregion

// #region 数独填充逻辑
/**
 * 找数字法
 * 通过已经存在的数字，然后去补全还未存在该宫格的数字
 * 如：宫1 和 宫3 中存在数字 2，而 宫2 中还无，则根据规则去判断 宫2 能否推导出 2 应该填写的位置
 */
function findNumber(
  gongArr: ShuDuNS.ShuDuData,
  targetNumber: number,
  setData: any
): boolean {
  // 找到哪几个宫有目标数字
  const targetIndex: number[] = []
  gongArr.forEach((gong, index) => {
    const gongValues = gong.map((item) =>
      isValidArray(item.value as number[]) ? 0 : item.value
    )
    if (gongValues.includes(targetNumber)) {
      targetIndex.push(index)
    }
  })
  // 使用这些宫，去得到哪些宫的数字可以进行填充（相关联的才能进行）
  let canFillGongIndex: number[] = []
  targetIndex.forEach((gongIndex) => {
    const relateIndex: number[] = RELATION_SHIP[gongIndex] as number[]
    canFillGongIndex.push(...relateIndex)
  })
  canFillGongIndex = Array.from(new Set(canFillGongIndex)).filter(
    (item) => !targetIndex.includes(item)
  )
  // 根据可填充的宫，去尝试推导结果
  for (let index = 0; index < canFillGongIndex.length; index++) {
    const gongIndex = canFillGongIndex[index]
    let fullIndex = getFullGongIndex()
    // 根据可填充的宫，去找有线索的宫
    const relateIndex = RELATION_SHIP[gongIndex].filter((item) =>
      targetIndex.includes(item)
    )
    relateIndex.forEach((item) => {
      // 有线索的宫与当前宫的关系，目标数字的索引，然后对当前宫进行排除
      const gongRelation = getGongRelation(item, gongIndex)

      const gongValues = gongArr[item].map((gongItem) => gongItem.value)
      const targetIndexInGong = gongValues.indexOf(targetNumber)
      if (targetIndexInGong === -1) return
      fullIndex = filterRelatedGong(fullIndex, targetIndexInGong, gongRelation)
    })
    // 排除完毕后，判断当前宫剩余的空格，如果只有一个，则可进行填写
    const restCell = fullIndex.filter(
      (item) =>
        !gongArr[gongIndex][item].value ||
        isValidArray(gongArr[gongIndex][item].value as number[])
    )
    if (restCell.length === 1) {
      setData(gongIndex, restCell[0], targetNumber)
      return true
    }
  }
  return false
}

/**
 * 找数字法的全流程
 */
export function findNumberAllProcess(
  originData: ShuDuNS.ShuDuData,
  setData: any,
  targetNumber?: number
) {
  const gongArr: ShuDuNS.ShuDuData = []
  // 按照宫的顺序，生成一个新的数组
  for (let index = 0; index < 9; index++) {
    const res = getGongData(originData, index)
    gongArr.push(res)
  }
  // 是否进行下一个大循环，如果当前循环设置了宫数据，则需要进行下一个大循环
  let needCoverAll = true
  // 设置宫数据，并修改原始数据
  function setGongData(gongIndex: number, cellIndex: number, value: number) {
    needCoverAll = true
    gongArr[gongIndex][cellIndex].value = value
    // 需要从宫转换成row、col对应值
    const rowIndex = Math.floor(gongIndex / 3) * 3 + Math.floor(cellIndex / 3)
    const colIndex = (gongIndex % 3) * 3 + (cellIndex % 3)
    console.log(
      `找数字法填充：目标数字-${value}，row-${rowIndex}，col-${colIndex}`
    )
    setData(rowIndex, colIndex, value)
  }

  // 遍历 1 - 9，然后进行找数字法
  function coverAll() {
    needCoverAll = false
    // 如果某个数据完成了填充，则需要对该目标数字重新进行找数字
    // 如果一轮下来，没有任何填充行为，则结束找数字法
    let targetNum = 1
    while (targetNum <= 9) {
      const isFilled = findNumber(gongArr, targetNum, setGongData)
      if (!isFilled) targetNum += 1
    }
  }

  // 如果有目标数字，则进行一个数字的填充
  if (targetNumber) {
    let coverTargetNumber = true
    let rangeCount = 0
    while (coverTargetNumber) {
      coverTargetNumber = findNumber(gongArr, targetNumber, setGongData)
      rangeCount += 1
    }
    // 说明没有找到数字
    if (rangeCount === 1) {
      console.log(`数字 ${targetNumber} 进行找数字法填充失败`)
    }
    return
  }

  while (needCoverAll) {
    coverAll()
  }
}

/**
 * 可能性法
 * 把每个格中所有空白格可能性的数字补充完全，再去判断是否有唯一值（这个最好是一种模式，比如开启之后，后续填入数字后，自动进行可能性修正）
 */
export function fillPossibilityNumber(
  originData: ShuDuNS.ShuDuData,
  setData: any
) {
  // 先把所有行、列、宫的数据记录下来
  const rowData = []
  const colData = []
  const gongData = []
  const cloneData = cloneDeep(originData)
  for (let index = 0; index < 9; index++) {
    const singleRowData = getRowData(cloneData, index)
    rowData.push(singleRowData)
    const singleColData = getColData(cloneData, index)
    colData.push(singleColData)
    const singleGongData = getGongData(cloneData, index)
    gongData.push(singleGongData)
  }
  // 标准值 1 - 9
  const standardData = new Array(9).fill(1).map((item, index) => index + 1)
  // 从左上到右下进行全覆盖
  for (let rowIndex = 0; rowIndex < 9; rowIndex++) {
    for (let colIndex = 0; colIndex < 9; colIndex++) {
      // 如果格子内已有值，则跳过
      if (
        !isValidArray(cloneData[rowIndex][colIndex].value as number[]) &&
        cloneData[rowIndex][colIndex].value
      )
        continue
      // 如果格子内没有值，则去计算缺啥，然后设置
      // 通过行、列、宫内已有的数据，去计算缺啥
      let existData: any[] = []
      const speRowData = rowData[rowIndex]
      const speColData = colData[colIndex]
      const speGongData =
        gongData[Math.floor(rowIndex / 3) * 3 + Math.floor(colIndex / 3)]
      existData.push(
        ...speRowData
          .filter((item) => !Array.isArray(item.value) && !!item.value)
          .map((item) => item.value)
      )
      existData.push(
        ...speColData
          .filter((item) => !Array.isArray(item.value) && !!item.value)
          .map((item) => item.value)
      )
      existData.push(
        ...speGongData
          .filter((item) => !Array.isArray(item.value) && !!item.value)
          .map((item) => item.value)
      )
      // 去重一下
      existData = existData.filter(
        (item, index) => existData.indexOf(item) === index
      )
      const restData = standardData.filter((item) => !existData.includes(item))
      // 然后设置数据
      if (restData.length > 1) {
        setData(rowIndex, colIndex, restData)
      } else if (restData.length === 1) {
        setData(rowIndex, colIndex, restData[0])
        const firstValue = restData[0]
        cloneData[rowIndex][colIndex].value = firstValue
        for (let index = 0; index < 9; index++) {
          const singleRowData = getRowData(cloneData, index)
          rowData.push(singleRowData)
          const singleColData = getColData(cloneData, index)
          colData.push(singleColData)
          const singleGongData = getGongData(cloneData, index)
          gongData.push(singleGongData)
        }
        rowIndex = 0
        colIndex = 0
      } else if (restData.length === 0) {
        console.error(
          `可能性法计算有误，${rowIndex} 行 ${colIndex} 列计算不出值`
        )
        return
      }
    }
  }
}

// #endregion

/**
 * 用来判断数据的数据是否准确
 * true 表示正确；false 表示有错误
 */
export function judgeShuDuError(originData: ShuDuNS.ShuDuData): boolean {
  // 通过判断是否共有 81 数字，再判断 1-9 各九个数字是否完整
  const allNumber: number[] = []
  for (let index = 0; index < originData.length; index++) {
    const row = originData[index]
    for (let j = 0; j < row.length; j++) {
      const cell = row[j]
      if (typeof cell.value === 'number' && cell.value) {
        allNumber.push(cell.value)
      } else {
        return false
      }
    }
  }
  if (allNumber.length < 81) return false
  // 排序一些数字，然后获取各个数字的 index，来判断
  const sortedNumber = allNumber.sort()
  let currentIndex = 0
  while (currentIndex < 9) {
    if (sortedNumber.indexOf(currentIndex + 1) !== currentIndex * 9) {
      currentIndex = 11
    }
    currentIndex += 1
  }
  if (currentIndex >= 11) return false
  return true
}
